Skip to content

uno: RsaUnwrap bring-up — OAEP endianness fix + non-CRT RSA private-key import - #602

Open
Rajib Dutta (radutta99) wants to merge 16 commits into
mainfrom
user/radutta/rsa-unwrap-bringup
Open

uno: RsaUnwrap bring-up — OAEP endianness fix + non-CRT RSA private-key import#602
Rajib Dutta (radutta99) wants to merge 16 commits into
mainfrom
user/radutta/rsa-unwrap-bringup

Conversation

@radutta99

Copy link
Copy Markdown
Contributor

Summary

Brings up the RSA paths of RsaUnwrap on the uno HSM firmware. Three changes:

  1. OAEP endianness fixrsa_oaep_decrypt now flips the mod_exp_priv result LE→BE before the RFC 8017 EME-OAEP decode. The uno PKA is little-endian native (LE ciphertext in, LE result out), but the OAEP encoded message 0x00 ‖ maskedSeed ‖ maskedDB is big-endian; the std PAL does the same flip around OpenSSL. Without it, every RsaUnwrap failed with RsaDecryptFailed (0x08700012). This fixes the AES-key unwrap path.

  2. Non-CRT RSA private-key import — implements rsa_priv_der_to_vault. Adds a small no_std DER parser (der_tlv, der_int, parse_rsa_priv_der) handling PKCS#8 PrivateKeyInfo and bare PKCS#1 RSAPrivateKey. The PKCS#8 AlgorithmIdentifier is validated to be rsaEncryption (OID 1.2.840.113549.1.1.1 + NULL params), mirroring mcr-hsm's Asn1RsaEncryptionInfo. The parsed n/e/d are assembled into the PKA vault operand [d(k) ‖ n(k) ‖ e(4)] little-endian — matching mcr-hsm's RsaPrivKey::to_pka_bytes and what rsa_priv_pub_key reads back. The stack scratch holding the private exponent is scrubbed with zeroize. CRT import returns UnsupportedCmd (deferred — needs PKA-derived n1q/n2p).

  3. OAEP oversized-output error contractrsa_oaep_decrypt now returns RsaInvalidKeyLength (not InvalidArg) when the recovered message exceeds the caller's output buffer, matching the std PAL. The shared key-unwrap path maps this to RsaUnwrapInvalidKek, so an oversized recovered KEK is now reported correctly.

Testing

Hardware (Manticore EVB):

  • rsa_unwrap_generated_key: 12/17 (was 9/17)
  • rsa_unwrap_smoke: 3/5 (was 2/5) — oversized_kek_smoke now passes
  • get_unwrapping_key: 7/7 (unchanged)

Emu: precheck --nextest --package azihsm_ddi_mbor_types --features emu --filter smoke --profile ci-emu-smoke → 94/94 passed.

fmt / clippy (-D warnings, ms-nightly) / taplo all clean.

Deferred to follow-up PRs

The remaining rsa_unwrap failures each map cleanly to one of:

  • CRT RSA import — needs PKA-derived n1q/n2p (mcr-hsm computes these separately).
  • uno ECC key import — blocks ecc_key_smoke, ecc_keys_with_key_tag.
  • OpenKey-by-tag — a full SDK-wide DDI (op 1015 has no handler); needs a shared PAL-trait + vault change (the uno vault Entry already carries a session_or_tag field).

Base

Stacked on #601 (user/radutta/get-unwrapping-key), which is not yet merged — this PR targets that branch, not main.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Brings up RSA unwrap support on uno firmware by aligning RSA-OAEP decode with the PKA’s little-endian behavior, adding non-CRT RSA private-key import from DER into the uno vault layout, and tightening an OAEP error contract to match the std PAL.

Changes:

  • Reverse the mod_exp_priv result (LE→BE) before OAEP unpadding to fix RSA-OAEP decrypt on uno.
  • Implement rsa_priv_der_to_vault for non-CRT RSA keys via a minimal PKCS#8/PKCS#1 DER parser and vault operand assembly, plus stack scratch scrubbing.
  • Return RsaInvalidKeyLength (not InvalidArg) when OAEP plaintext exceeds the caller buffer; add zeroize dependency for firmware PAL.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
fw/plat/uno/fw/pal/src/crypto/rsa.rs Adds DER parsing + non-CRT RSA private-key import; fixes OAEP endianness and output-length error contract.
fw/plat/uno/fw/pal/Cargo.toml Adds zeroize as a dependency for the uno PAL crate.
fw/plat/uno/fw/Cargo.toml Adds zeroize (no-default-features) to the uno firmware workspace dependencies.

Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated
Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (1)

fw/plat/uno/fw/pal/src/crypto/rsa.rs:175

  • In the PKCS#8 path, the code descends into the privateKey OCTET STRING but does not verify that the inner RSAPrivateKey SEQUENCE consumes the entire OCTET STRING. Without that check, extra bytes inside the OCTET STRING are silently accepted.
        let (ot, oct_start, _ol, _on) = der_tlv(der, after_alg)?;
        if ot != 0x04 {
            return None;
        }
        let (st, inner_start, _sl, _sn) = der_tlv(der, oct_start)?;

Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated
Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

fw/plat/uno/fw/pal/src/crypto/rsa.rs:332

  • buf[vault_len..].zeroize() scrubs a plain &mut [u8] using the zeroize crate, but this repo has a DMA-specific wipe primitive (DmaBuf::zeroize) that uses per-byte volatile writes + a compiler fence (fw/pal/traits/src/alloc.rs:148-160). For DMA-backed buffers, prefer wiping via DmaBuf::zeroize to match the established secret-scrub pattern.
        buf[vault_len..].zeroize();

Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated
Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

fw/plat/uno/fw/pal/src/crypto/rsa.rs:199

  • parse_rsa_priv_der currently only parses the PKCS#1 version, n, e, d integers and then returns success without confirming that the mandatory remaining PKCS#1 fields (p, q, dp, dq, qinv) are present and that the key SEQUENCE is fully consumed. This makes the parser accept malformed/truncated RSA keys (or extra trailing bytes inside the SEQUENCE) even though the comment says those fields “follow”, and it weakens input validation for untrusted recovered key material.
    // n, e, d in order (p, q, dp, dq, qinv follow but are unused for non-CRT).
    let (ns, nl, after_n) = der_int(der, p)?;
    let (es, el, after_e) = der_int(der, after_n)?;
    let (ds, dl, _after_d) = der_int(der, after_e)?;
    Some(((ns, nl), (es, el), (ds, dl)))

uno's rsa_oaep_decrypt fed the LE ciphertext to the LE-native mod_exp_priv
(correct) but then ran the RFC 8017 EME-OAEP decode directly on the LE result,
checking em[0] (the LSB) for the 0x00 leading byte and MGF1-unmasking
byte-reversed data -> RsaDecryptFailed for every real unwrap. Reverse the
mod-exp result LE->BE first (matching the std PAL's flip around OpenSSL) so the
OAEP decode sees the big-endian EM = 0x00 || maskedSeed || maskedDB.
Implement rsa_priv_der_to_vault on the uno PAL so RsaUnwrap can import a recovered RSA private key. Adds a no_std DER parser (der_tlv, der_int, parse_rsa_priv_der) handling PKCS#8 PrivateKeyInfo and bare PKCS#1 RSAPrivateKey. The PKCS#8 algorithm is validated to be rsaEncryption (OID 1.2.840.113549.1.1.1 + NULL params), mirroring mcr-hsm Asn1RsaEncryptionInfo. The parsed n/e/d are assembled into the PKA vault operand [d(k) || n(k) || e(4)] little-endian, matching mcr-hsm RsaPrivKey::to_pka_bytes and what rsa_priv_pub_key reads back.

The stack scratch that holds the private exponent is scrubbed with zeroize (volatile writes, not elidable). Non-CRT only; CRT import returns UnsupportedCmd (needs PKA-derived n1q/n2p, deferred).

HW-verified: unblocks rsa_unwrap_generated_key rsa_key, rsa_key_sizes, and incorrect_input_key_usage (12/17 in the suite now pass).
rsa_oaep_decrypt returned InvalidArg when the recovered message was larger than the caller output buffer. The std PAL returns RsaInvalidKeyLength here, and the shared key-unwrap path maps that to RsaUnwrapInvalidKek. Align uno with that contract so an oversized recovered KEK surfaces as RsaUnwrapInvalidKek.

HW-verified: rsa_unwrap_smoke oversized_kek_smoke now passes (smoke 3/5; remaining 2 need ECC/CRT import).
Address PR review comments: der_int now rejects negative DER INTEGERs (MSB set on the first content byte with no 0x00 sign pad) and non-minimal encodings, since it parses untrusted recovered key material; the integer zero (PKCS#8/PKCS#1 version field) is still accepted. rsa_oaep_decrypt now scrubs the secret-bearing em DMA scratch via DmaBuf::zeroize on every exit path (including ?-propagated errors) by running the fallible OAEP decode in an inner block.
Address PR review: rsa_priv_der_to_vault now scrubs the tail of buf after rewriting the vault operand (the leftover source DER still holds secret CRT components p/q/dp/dq/qinv; the scoped DMA buffer is reused without automatic wiping). parse_rsa_priv_der now rejects trailing garbage: the outer SEQUENCE must span the entire input (der.len()), and the PKCS#8 privateKey OCTET STRING must wrap exactly the inner RSAPrivateKey SEQUENCE (material is exact-length per unwrap_key returning payload_buf[..payload_len]).
Address PR review: der_tlv now rejects non-minimal long-form definite lengths. The most-significant length byte must be non-zero (no leading-zero padding), and long form must not encode a length that fits in short form (len >= 0x80). This tightens the parser against malformed untrusted key material, matching strict DER decoders.
Address PR review: rsa_priv_der_to_vault returned early on crt==true, DER parse failure, and size validation without wiping buf, leaving the recovered plaintext RSA private-key DER resident in reused DMA SRAM. Now zeroize buf on every early-error return. The success path is unchanged (it overwrites buf[..vault_len] with the vault operand and scrubs buf[vault_len..]).
…tent

Address PR review: (1) rsa_priv_der_to_vault now rejects (scrub + InvalidArg) when the vault operand length (2*modulus_len+4) exceeds buf.len(), which a malformed/truncated PKCS#1 (e.g. a very short d) could trigger, avoiding an out-of-bounds panic on the in-place slices. (2) parse_rsa_priv_der now requires the AlgorithmIdentifier NULL parameters to consume the rest of the algorithm SEQUENCE (null_next == after_alg), rejecting extra trailing fields.
@radutta99
Rajib Dutta (radutta99) force-pushed the user/radutta/rsa-unwrap-bringup branch from 0f88cf9 to 4c437bd Compare July 30, 2026 01:46
@radutta99
Rajib Dutta (radutta99) changed the base branch from user/radutta/get-unwrapping-key to main July 30, 2026 01:47
Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated
Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated
Comment thread fw/plat/uno/fw/pal/src/asn1.rs
Comment thread fw/plat/uno/fw/pal/Cargo.toml Outdated
Rajib Dutta (radutta99) and others added 2 commits August 1, 2026 02:14
Drop the bare PKCS#1 RSAPrivateKey fallback in parse_rsa_private_key. The recovered RsaUnwrap wire key is always a PKCS#8 PrivateKeyInfo (the format the test collateral is generated in), so the untested bare-PKCS#1 path is removed, reducing the accepted-input surface. Addresses review feedback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c
Replace the >1 KiB stack scratch buffer with a fully in-place assembly: the vault operand [d|n|e] overlaps the DER field offsets it reads from, so d is first staged into the tail of buf (which the caller scrubs) via overlap-safe copy_within, then the fields are moved and reversed big-endian to little-endian into the front. Field offsets are recovered from the der UintRef sub-slices. Removes write_le, assemble_rsa_operand, MAX_RSA_MODULUS_LEN, and the now-unused HsmAlloc and RsaPrivateKeyAsn1 imports. Also adds a TODO on the CRT stub. Addresses review feedback.

HW (Manticore EVB): rsa_unwrap non-CRT RSA import passes for 2k/3k/4k (rsa_key + rsa_key_sizes); 15/22 rsa_unwrap matches the baseline, no regression.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c
Copilot AI review requested due to automatic review settings August 1, 2026 02:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Suppressed comments (1)

fw/plat/uno/fw/pal/src/asn1.rs:103

  • parse_rsa_private_key currently rejects a bare PKCS#1 RSAPrivateKey (it only accepts PKCS#8 PrivateKeyInfo). This contradicts both the module docs ("PKCS#8 / PKCS#1") and the PR description, which says bare PKCS#1 is supported. Either add a PKCS#1 fallback parse or update the docs/PR description to match the actual accepted formats.
pub(crate) fn parse_rsa_private_key(der_bytes: &[u8]) -> Option<RsaPrivateKeyAsn1<'_>> {
    // The recovered wire key is always a PKCS#8 PrivateKeyInfo (the format the
    // RsaUnwrap collateral is generated in); a bare PKCS#1 RSAPrivateKey is not
    // accepted.
    let pki = RsaPrivateKeyInfo::from_der(der_bytes).ok()?;

Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated
Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated
Comment thread fw/plat/uno/fw/pal/Cargo.toml Outdated
Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated
- Move the `der` dependency to `[workspace.dependencies]` and reference it
  as `der = { workspace = true }`, matching every other external crate in
  the uno firmware workspace.

- Drop `RsaOperandLayout::e_len`. The vault operand's exponent slot is a
  fixed `EXP_WIRE_LEN` field, so carrying both a 4-byte array and a length
  expressed the same thing twice. `e` is now converted to its little-endian
  wire form once, while parsing, and assembly is a single `copy_from_slice`
  instead of copy/reverse/pad. `EXP_WIRE_LEN` is hoisted to module scope and
  used everywhere the width was previously spelled `4`.

- Remove the explicit zeroize calls. The per-IO teardown scrub wipes the
  whole slot, so these are redundant and would only have to be deleted
  again; a comment records that the buffer is intentionally left dirty.

- Removing `em.zeroize()` also removes the reason for the inner `async`
  block in `rsa_oaep_decrypt` — it existed solely to funnel every exit path
  into one scrub — so the decode returns to a flat `?` flow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c
Copilot AI review requested due to automatic review settings August 5, 2026 05:36

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Suppressed comments (1)

fw/plat/uno/fw/pal/src/asn1.rs:103

  • The PR description and this module’s top-level docs claim the RSA decoder handles both PKCS#8 PrivateKeyInfo and bare PKCS#1 RSAPrivateKey, but parse_rsa_private_key currently rejects bare PKCS#1 (“not accepted”). Either the docs/PR description should be updated, or the parser should accept PKCS#1 as a fallback to match the stated contract.
pub(crate) fn parse_rsa_private_key(der_bytes: &[u8]) -> Option<RsaPrivateKeyAsn1<'_>> {
    // The recovered wire key is always a PKCS#8 PrivateKeyInfo (the format the
    // RsaUnwrap collateral is generated in); a bare PKCS#1 RSAPrivateKey is not
    // accepted.
    let pki = RsaPrivateKeyInfo::from_der(der_bytes).ok()?;

Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs
Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs
Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated
Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs Outdated
Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs
Copilot AI review requested due to automatic review settings August 5, 2026 17:32

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Suppressed comments (3)

fw/plat/uno/fw/pal/src/crypto/rsa.rs:266

  • This comment claims the recovered plaintext DER in buf is scrubbed by a per-IO teardown, but UnoHsmIoController::drop_io currently only frees the slot (fw/plat/uno/fw/pal/src/io.rs:171-174) and reset_io_alloc only rewinds watermarks (fw/plat/uno/fw/pal/src/alloc.rs:94-98). As written, recovered RSA private key material can persist in the reused DMA buffer after both success and error paths.
        // Note: `buf` holds recovered plaintext DER (and, after assembly, the
        // staged `d` plus the leftover CRT components `p`/`q`/`dp`/`dq`/`qinv`)
        // on every path through this function. It is not scrubbed here — the
        // per-IO teardown scrub wipes the whole slot, so a local wipe would be
        // redundant work that has to be removed again.

fw/plat/uno/fw/pal/src/crypto/rsa.rs:273

  • rsa_priv_der_to_vault returns early on CRT requests, parse failures, and size validation failures without scrubbing buf, and the success path leaves the DER tail (including CRT components) resident in SRAM. Since IO teardown does not currently wipe IO buffers, this leaks plaintext private-key material across IO reuse.
        if crt {
            return Err(HsmError::UnsupportedCmd);
        }

fw/plat/uno/fw/pal/src/asn1.rs:103

  • The PR description says the DER parser accepts both PKCS#8 PrivateKeyInfo and bare PKCS#1 RSAPrivateKey, but this function currently rejects bare PKCS#1 by unconditionally decoding RsaPrivateKeyInfo first. Either update the behavior to fall back to PKCS#1, or adjust the PR description/docs to match.
pub(crate) fn parse_rsa_private_key(der_bytes: &[u8]) -> Option<RsaPrivateKeyAsn1<'_>> {
    // The recovered wire key is always a PKCS#8 PrivateKeyInfo (the format the
    // RsaUnwrap collateral is generated in); a bare PKCS#1 RSAPrivateKey is not
    // accepted.
    let pki = RsaPrivateKeyInfo::from_der(der_bytes).ok()?;

The endian reversal between DER/SEC1 big-endian integers and the PKA/vault
little-endian layout was written out as a loop or as copy-then-reverse at
every call site. Add a single `reverse_copy` in `crypto/mod.rs` and use it
from its two consumers.

In the RSA operand assembly it covers the public exponent, and the private
exponent, whose staged copy is disjoint from the front of the buffer, so
the previous copy-then-reverse-in-place collapses into one pass. The
modulus keeps `copy_within` plus an in-place reverse because its source and
destination can overlap and `reverse_copy` needs disjoint slices. Verified
byte-identical to the previous assembly across 60 combinations of modulus
size, private-exponent length and field offsets.

It also folds the four copy-then-reverse pairs in the deterministic ECC
signing path, which all copy between distinct buffers.

The helper lives in the uno PAL rather than in `azihsm_fw_hsm_pal_traits`:
it is a plain function, not part of any PAL contract, so a traits crate is
the wrong home. Equivalent private copies remain in the std PAL and in the
core evidence / key-report / attest-key handlers, which cannot reach a
uno-local helper; deduplicating those would need a shared utility crate and
is left alone here.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 2444329e-909e-4ced-b5df-5a40fb65bb6c
Copilot AI review requested due to automatic review settings August 5, 2026 20:11
@radutta99
Rajib Dutta (radutta99) force-pushed the user/radutta/rsa-unwrap-bringup branch from 596b73e to 5f9a843 Compare August 5, 2026 20:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Suppressed comments (4)

fw/plat/uno/fw/pal/src/crypto/rsa.rs:279

  • Error exits from rsa_priv_der_to_vault (crt, parse failure, size check) return without scrubbing buf, which still contains recovered plaintext RSA private-key DER. Since DmaBuf contents persist after the scoped allocator rewinds, this can leak key material in SRAM after a failed import.
        if crt {
            return Err(HsmError::UnsupportedCmd);
        }
        // Parse + validate + capture the field layout. The `key` borrow of `buf`
        // (held by the decoded `UintRef`s) is released before the in-place
        // rewrite below.
        let Some(layout) = rsa_operand_layout(&buf[..]) else {
            return Err(HsmError::InvalidArg);
        };

fw/plat/uno/fw/pal/src/crypto/rsa.rs:290

  • After in-place operand assembly, buf[vault_len..] still contains staged d and leftover DER (including CRT components like p/q/dp/dq/qinv). Without a guaranteed per-IO scrub, this should be zeroized before returning.
        assemble_rsa_operand_in_place(&mut buf[..], &layout);
        Ok((vault_len, k))

fw/plat/uno/fw/pal/src/asn1.rs:103

  • parse_rsa_private_key currently rejects bare PKCS#1 RSAPrivateKey inputs (it only parses PKCS#8 PrivateKeyInfo). This contradicts both the PR description (“PKCS#8 and bare PKCS#1”) and this module’s header doc that states PKCS#8 / PKCS#1 support.
pub(crate) fn parse_rsa_private_key(der_bytes: &[u8]) -> Option<RsaPrivateKeyAsn1<'_>> {
    // The recovered wire key is always a PKCS#8 PrivateKeyInfo (the format the
    // RsaUnwrap collateral is generated in); a bare PKCS#1 RSAPrivateKey is not
    // accepted.
    let pki = RsaPrivateKeyInfo::from_der(der_bytes).ok()?;

fw/plat/uno/fw/pal/src/crypto/rsa.rs:117

  • rsa_operand_layout only bounds-checks e by byte width, but it doesn’t validate the exponent value itself (e.g., it would accept an empty/zero exponent or an even exponent). Since this is decoding untrusted imported key material, reject invalid RSA public exponents to avoid importing unusable/invalid keys.
    let n = key.modulus.as_bytes();
    let e = key.public_exponent.as_bytes();
    let d = key.private_exponent.as_bytes();
    let modulus_len = n.len();
    if !matches!(modulus_len, 256 | 384 | 512) || e.len() > EXP_WIRE_LEN || d.len() > modulus_len {
        return None;
    }

Comment thread fw/plat/uno/fw/pal/src/crypto/rsa.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants